package cn.weekdragon.gscore.adapter.bot.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.weekdragon.gscore.adapter.EventChannel;
import cn.weekdragon.gscore.adapter.bot.BotManager;
import cn.weekdragon.gscore.adapter.bot.WsReverseBot;
import cn.weekdragon.gscore.adapter.model.EventExt;
import cn.weekdragon.gscore.adapter.model.SendResp;
import cn.weekdragon.gscore.adapter.model.enums.EventEnum;
import cn.weekdragon.gscore.adapter.model.enums.UserTypeEnum;
import cn.weekdragon.gscore.adapter.model.message.*;
import cn.weekdragon.gscore.adapter.utils.HttpUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.java_websocket.handshake.ServerHandshake;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
public class RedBot extends WsReverseBot {

    private final AtomicInteger msgSeq = new AtomicInteger(RandomUtil.randomInt(0, Short.MAX_VALUE));
    private String connectId;
    private final String apiAddress;

    public RedBot(String wsReverse,String botName, String authorization) throws URISyntaxException {
        super(wsReverse,botName, authorization);
        this.apiAddress = wsReverse.replace("ws","http") + "/api/";
    }

    @Override
    public EventExt prepareEventExt(String message) {
        JSONObject redPacket = JSON.parseObject(message);
        String type = redPacket.getString("type");
        if("meta::connect".equals(type)){
            JSONObject payload = redPacket.getJSONObject("payload");
            String connectId = payload.getJSONObject("authData").getString("account");
            if(StrUtil.isNotEmpty(connectId)){
                this.connectId = connectId;
                BotManager.addBot(this);
                log.info("redBot({})反向 ws 连接上机器人，id = {} 协议名称：{} 协议版本:{}",botName,connectId,payload.getString("name"),payload.getString("version"));
            }
        }else if("message::recv".equals(type)){
            JSONArray payloadArray = redPacket.getJSONArray("payload");
            JSONObject payload = payloadArray.getJSONObject(0);
            EventExt ext = new EventExt();
            ext.setBot(this);
            ext.setConnectId(connectId);
            ext.setMessageId(payload.getString("msgId"));
            ext.setUserId(payload.getString("senderUin"));
            ext.setUserType(UserTypeEnum.QQ.getType());
            ext.setMsgSeq(payload.getString("msgSeq"));
            List<Message> messages = parseMessage(payload.getJSONArray("elements"));
            MessageChain messageChain = new MessageChain();
            messageChain.setMessageChain(messages);
            ext.setInMessage(messageChain);
            ext.setSenderName(payload.getString("sendMemberName"));
            ext.setGroupId(payload.getString("peerUin"));
            // 1: 私聊 2: 群聊
            Integer chatType = payload.getInteger("chatType");
            if(chatType == 1){
                ext.setType(EventEnum.PRIVATE_MESSAGE.getType());
            }else if(chatType == 2){
                ext.setType(EventEnum.GROUP_MESSAGE.getType());
            }else{
                log.error("未知的 chatType：{}",chatType);
                return null;
            }
            return ext;
        } else {
            log.info("未知的消息类型：{}", message);
        }
        return null;
    }

    @Override
    public SendResp sendGroupMessage(String groupId, Message message) {
        SendResp sendResp = new SendResp(this);
        Pair<JSONArray, JSONArray> pair = convertMessage(message);
        JSONArray key = pair.getKey();
        JSONArray value = pair.getValue();
        if(CollectionUtil.isNotEmpty(key)){
            JSONObject payload = new JSONObject();
            JSONObject peer = new JSONObject();
            payload.put("peer",peer);
            // 1: 私聊 2: 群聊
            peer.put("chatType",2);
            peer.put("peerUin",String.valueOf(groupId));
            payload.put("elements",key);
            String s = callApi("message/send", payload.toJSONString());
            JSONObject res = JSON.parseObject(s);
            sendResp.setMessageId(res.getLong("msgId"));
        }else if(CollectionUtil.isNotEmpty(value)){
            // 发送转发消息
            sendResp.setMessageId(sendForwardMessage(value));
        }
        return sendResp;
    }

    @Override
    public SendResp sendPrivateMessage(String userId, Message message) {
        SendResp sendResp = new SendResp(this);
        Pair<JSONArray, JSONArray> pair = convertMessage(message);
        JSONArray key = pair.getKey();
        JSONArray value = pair.getValue();
        if(Objects.nonNull(key)){
            JSONObject payload = new JSONObject();
            JSONObject peer = new JSONObject();
            payload.put("peer",peer);
            // 1: 私聊 2: 群聊
            peer.put("chatType",1);
            peer.put("peerUin",String.valueOf(userId));
            payload.put("elements",key);
            String s = callApi("message/send", payload.toJSONString());
            JSONObject res = JSON.parseObject(s);
            sendResp.setMessageId(res.getLong("msgId"));
        }else if(CollectionUtil.isNotEmpty(value)){
            // 发送转发消息
            sendForwardMessage(value);
        }
        return sendResp;
    }

    @Override
    public String getBotId() {
        return connectId;
    }


    @Override
    public void onOpen(ServerHandshake handshakedata) {
        RedPacket redPacket = new RedPacket();
        redPacket.type = "meta::connect";
        JSONObject tokenObj = new JSONObject();
        tokenObj.put("token",authorization);
        redPacket.payload = tokenObj;
        log.info("redBot({}):发送认证协议",this.botName);
        send(JSON.toJSONString(redPacket));
    }

    @Override
    public void onMessage(String message) {
        log.info("redBot({}): receive message:{}",this.botName,message);
        EventChannel.receiveMessage(this,message);
    }

    public Long sendForwardMessage(JSONArray msgElements){
        Map<String,Object> payload = new HashMap<>();
        payload.put("msgElements",msgElements);
        JSONObject srcContact = new JSONObject();
        srcContact.put("chatType",2L);
        srcContact.put("peerUin",EventExt.get().getGroupId());
        JSONObject dstContact = new JSONObject();
        dstContact.put("chatType",2L);
        dstContact.put("peerUin",EventExt.get().getGroupId());
        payload.put("srcContact",srcContact);
        payload.put("dstContact",dstContact);
        String body = JSON.toJSONString(payload);
        String s = callApi("message/unsafeSendForward", body);
        JSONObject res = JSON.parseObject(s);
        return res.getLong("msgId");
    }

    private String callApi(String api,String body){
        String url = apiAddress + api;
        log.info("red api call url:{},body:{}",url,body);
        Map<String,Object> headers = new HashMap<>(1);
        headers.put("Authorization", "Bearer " + authorization);
        String s = HttpUtils.postJson(url, headers,body);
        log.info("red api call res:{}",s);
        return s;
    }

    public void send(Object object) {
        send(JSON.toJSONString(object));
    }

    public void send(String text) {
        this.wsClient.send(StandardCharsets.UTF_8.encode(text));
    }


    private Pair<JSONArray,JSONArray> convertMessage(Message message) {
        JSONArray normalArray = new JSONArray();
        JSONArray forwardArray = new JSONArray();
        List<Message> list = message.toList();
        EventExt ext = EventExt.get();
        for (Message singleMessage : list) {
            JSONObject element = new JSONObject();
            if(singleMessage instanceof Plain plain){
                element.put("elementType",1);
                JSONObject textElement = new JSONObject();
                textElement.put("content",plain.getText()+"\n");
                element.put("textElement",textElement);
                normalArray.add(element);
            }else if(singleMessage instanceof At at){
                if(Objects.isNull(ext)){
                    continue;
                }
                element.put("elementType",1);
                JSONObject textElement = new JSONObject();
                textElement.put("content", "@"+ ext.getSenderName());
                textElement.put("atType",2);
                textElement.put("atNtUin",at.getTarget());
                element.put("textElement",textElement);
                normalArray.add(element);
            }else if(singleMessage instanceof Image image){
                String base64 = image.getBase64();
                byte[] imageData = null;
                if(StrUtil.isNotEmpty(base64)){
                    imageData = Base64.getDecoder().decode(base64);
                }else{
                    imageData = HttpUtils.getContent(image.getUrl());
                }
                Map<String,Object> headers = new HashMap<>(1);
                headers.put("Authorization", "Bearer " + authorization);
                String s = HttpUtils.uploadFile(apiAddress + "upload", imageData, headers);
                log.info("red 上传文件结果：{}",s);
                JSONObject imageRes = JSON.parseObject(s);
                JSONObject imageInfo = imageRes.getJSONObject("imageInfo");
                element.put("elementType",2);
                JSONObject picElement = new JSONObject();
                element.put("picElement",picElement);
                picElement.put("md5HexStr",imageRes.getString("md5"));
                picElement.put("fileSize",imageRes.getString("fileSize"));
                picElement.put("picHeight",imageInfo.getInteger("height"));
                picElement.put("picWidth",imageInfo.getInteger("width"));
                String ntFilePath = imageRes.getString("ntFilePath");
                String fileName = ntFilePath.substring(ntFilePath.lastIndexOf("\\") + 1);
                picElement.put("fileName",fileName);
                picElement.put("sourcePath", ntFilePath);
                normalArray.add(element);
            }else if(singleMessage instanceof MessageChain chain){
                log.info("chain = {}",JSON.toJSONString(chain));
                Pair<JSONArray, JSONArray> localRes = convertMessage(chain);
                JSONArray localNormal = localRes.getKey();
                JSONArray localForward = localRes.getValue();
                if (CollectionUtil.isNotEmpty(localNormal)) {
                    normalArray.addAll(localNormal);
                }
                if (CollectionUtil.isNotEmpty(localForward)) {
                    forwardArray.addAll(localForward);
                }
            }else if(singleMessage instanceof ForwardNode forwardNode){
                JSONObject node = new JSONObject();
                JSONObject head = buildHeader();
                JSONObject content = buildFWContent();
                node.put("head",head);
                node.put("content",content);
                node.put("body",buildFWBody(forwardNode.getContent()));
                forwardArray.add(node);
            }
        }
        return new Pair<>(normalArray,forwardArray);
    }

    private JSONObject buildHeader() {
        JSONObject head = new JSONObject(true);
        head.put("field2","u_PmxGsJErxkwN0ilC07NLWw");
        JSONObject field8 = new JSONObject(true);
        field8.put("field1", EventExt.get().getGroupId());
        field8.put("field4","Q群管家");
        head.put("field8",field8);
        return head;
    }

    private JSONObject buildFWBody(Message content) {
        JSONObject body = new JSONObject(true);
        JSONObject richText = new JSONObject(true);
        body.put("richText",richText);
        richText.put("elems",convertFWMessage(content.toList()));
        return body;
    }

    private JSONObject buildFWContent() {
        JSONObject content = new JSONObject(true);
        content.put("field1",82);
        content.put("field4", RandomUtil.randomInt());
        content.put("field5", msgSeq.getAndIncrement());
        content.put("field6",System.currentTimeMillis()/1000);
        content.put("field7",1);
        content.put("field8",0);
        content.put("field9",0);
        JSONObject field15 = new JSONObject(true);
        field15.put("field1",0);
        field15.put("field2",0);
        content.put("field15", field15);
        return content;
    }

    private JSONArray convertFWMessage(List<Message> items) {
        JSONArray elems = new JSONArray();
        for (Message message : items) {
            if (message instanceof MessageChain chain) {
                elems.addAll(convertFWMessage(chain.toList()));
            } else if (message instanceof Plain plain) {
                JSONObject elem = new JSONObject(true);
                JSONObject text = new JSONObject(true);
                elem.put("text", text);
                text.put("str", plain.getText());
                elems.add(elem);
            } else if (message instanceof Image image) {
                String base64 = image.getBase64();
                byte[] imageData = null;
                if (StrUtil.isNotEmpty(base64)) {
                    imageData = Base64.getDecoder().decode(base64);
                } else {
                    imageData = HttpUtils.getContent(image.getUrl());
                }
                Map<String, Object> headers = new HashMap<>(1);
                headers.put("Authorization", "Bearer " + authorization);
                String s = HttpUtils.uploadFile(wsReverse.replace("ws", "http") + "/api/upload", imageData, headers);
                log.info("red 上传文件结果：{}", s);
                JSONObject imageRes = JSON.parseObject(s);
                JSONObject imageInfo = imageRes.getJSONObject("imageInfo");
                JSONObject elem = new JSONObject(true);
                JSONObject customFace = new JSONObject(true);
                elem.put("customFace", customFace);
                elems.add(elem);
                String md5 = imageRes.getString("md5");
                String ntFilePath = imageRes.getString("ntFilePath");
                String extName = ntFilePath.substring(ntFilePath.lastIndexOf("."));
                customFace.put("filePath", convertImageMD5Path(md5, extName));
                customFace.put("fileId", RandomUtil.randomInt(0, Short.MAX_VALUE));
                customFace.put("serverIp", -1740138629);
                customFace.put("serverPort", 80);
                customFace.put("fileType", 1001);
                customFace.put("useful", 1);
                byte[] byteArray = new byte[0];
                try {
                    byteArray = Hex.decodeHex(md5);
                    List<Integer> md5Arr = new ArrayList<>();
                    for (int i = 0; i < byteArray.length; i++) {
                        md5Arr.add(byteArray[i] & 0xFF);
                    }
                    customFace.put("md5", md5Arr);
                } catch (DecoderException e) {
                    throw new RuntimeException(e);
                }
                customFace.put("imageType", 1001);
                Integer height = imageInfo.getInteger("height");
                customFace.put("height", height);
                Integer width = imageInfo.getInteger("width");
                customFace.put("width", width);
                customFace.put("size", height * width);
                customFace.put("origin", 0);
                customFace.put("thumbWidth", 0);
                customFace.put("thumbHeight", 0);
                customFace.put("pbReserve", Arrays.asList(2, 0));
            }
        }
        return elems;
    }
    private String convertImageMD5Path(String md5, String extName) {
        StringBuilder sb = new StringBuilder(md5.toUpperCase());
        sb.insert(20,"-");
        sb.insert(16,"-");
        sb.insert(12,"-");
        sb.insert(8,"-");
        sb.insert(0,"{");
        sb.append("}").append(extName);
        return sb.toString();
    }

    private List<Message> parseMessage(JSONArray elements) {
        if(CollectionUtil.isEmpty(elements)){
            return Collections.emptyList();
        }
        List<Message> result = new ArrayList<>();
        for (int i = 0; i < elements.size(); i++) {
            JSONObject item = elements.getJSONObject(i);
            Integer elementType = item.getInteger("elementType");
            if (elementType == 1) {
                // 文本 textElement
                JSONObject textElement = item.getJSONObject("textElement");
                Integer type = textElement.getInteger("atType");
                String content = textElement.getString("content");
                if(type == 0){
                    // 普通文本消息
                    result.add(new Plain(content));
                }else if(type == 2){
                    //  at 某人
                    At at = new At(textElement.getString("atNtUin"));
                    at.setDisplay(content);
                    result.add(at);
                }else{
                    log.info("未知的文本消息：{}",item.toJSONString());
                }
            }else if (elementType == 6){
                // 表情 faceElement

            }else if(elementType == 7){
                Quote quote = new Quote();
                // reply
                JSONObject replyElement = item.getJSONObject("replyElement");
                quote.setMessageId(replyElement.getString("replayMsgSeq"));
                result.add(quote);
            }
        }
        return result;
    }

    @Data
    public static class  RedPacket{
        private String type;

        private Object payload;
    }

}
